import sys, os

from collections import defaultdict
from common.namedtuple import namedtuple
from datetime import datetime

from google.appengine.api import users
from google.appengine.ext import db

class Root(db.Model):
    user = db.UserProperty()
    last_task_switch = db.ReferenceProperty()

class Task(db.Model):
    root = db.ReferenceProperty(Root)
    parent_task = db.SelfReferenceProperty()
    title = db.StringProperty()
    full_title = db.StringListProperty()
    created = db.DateTimeProperty()
    deleted = db.BooleanProperty(default=False)

class TaskSwitch(db.Model):
    root = db.ReferenceProperty(Root)
    started = db.DateTimeProperty()
    stopped = db.DateTimeProperty()
    task = db.ReferenceProperty(Task)
    comment = db.StringProperty()

    def duration(self):
        return self.stopped - self.started

def switch_task(context, new_task):
    def unsafe_switch_task():
        if (
            not context.root.last_task_switch
            or new_task.key().id() != context.root.last_task_switch.task.key().id()
        ):
            assert new_task.root.key().id() == context.root.key().id()
            now = datetime.now()
            if context.root.last_task_switch:
                context.root.last_task_switch.stopped = now
                context.root.last_task_switch.put()
            new_switch = TaskSwitch(
                root = context.root,
                started = now,
                stopped = None,
                task = new_task,
                parent = context.root,
            )
            new_switch.put()
            context.root.last_task_switch = new_switch
            context.root.put()

    db.run_in_transaction(unsafe_switch_task)

def create_task(context, title, parent_task=None):
    if parent_task != None:
        parent_task = Task.get_by_id(parent_task, parent=context.root)
        full_title = parent_task.full_title + [title]
    else:
        full_title = [title]
        parent_task = None
    task = Task(
        root = context.root,
        title = title,
        full_title = full_title,
        created = datetime.now(),
        parent_task = parent_task,
        parent = context.root,
    )
    task.put()

def update_task(context, task, title=None, parent_task=None):
    if title != None:
        task.title = title
        task.full_title[-1] = task.title
    if parent_task != None:
        parent_task = Task.get_by_id(parent_task, parent=context.root)
        if parent_task == None:
            raise Exception('Bad parent task')
        task.parent_task = parent_task
    task.put()

def get_tasks_tree(context):
    def parent_id(task):
        if task.parent_task is None:
            return None
        else:
            return task.parent_task.key().id()

    def subtree(parent2task, root_task, level=0):
        for task in parent2task[root_task]:
            yield level, task
            for sublevel, subtask in subtree(parent2task, task, level=level+1):
                yield sublevel, subtask

    tasks = list(Task.gql(
        "WHERE ANCESTOR IS :1 AND deleted=:2",
        context.root,
        False
    ))
    tasks_dict = dict((task.key().id(), task) for task in tasks)
    parent2task = defaultdict(list)
    for task_id, task in tasks_dict.iteritems():
        parent2task[parent_id(task)].append(task_id)

    for level, task_id in subtree(parent2task, None):
        yield dict(level=level, task=tasks_dict[task_id])

ReqContext = namedtuple('ReqContext', 'user root')
def get_context(handler):
    user = users.get_current_user()
    if user:
        root = Root.gql("WHERE user=:1", user).get()
        if not root:
            root = Root(user=user, last_task_switch=None)
            root.put()
        return ReqContext(user, root)
    else:
        handler.redirect(users.create_login_url(handler.request.uri))
        return None
